#include <iostream>
#include <ctype.h>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <fstream>
#include <sstream>

using std::cout;
using std::endl;
using std::cerr;
using std::cin;
using std::string;
using std::map;
using std::set;
using std::vector;
using std::ifstream;
using std::ofstream;
using std::istringstream;

/*
读取用户指定的任意文本文件【当前目录下的china_daily.txt】，允许用户从该文件中查找单词。
查询的结果是该单词出现的次数
列出每次出现所在的行。
如果某单词在同一行中多次出现，程序将只显示该行一次。
行号按升序显示。

要求：
a. 它必须允许用户指明要处理的文件名字。

b. 程序将存储该文件的内容，以便输出每个单词所在的原始行。
vector<string> _textlines;   记录每一行

c. 它必须将每一行分解为各个单词，并记录每个单词所在的所有行。
在输出行号时，应保证以升序输出，并且不重复。

map<string, set<int>> _wordlines; 这个单词出现过的所有行
map<string, int> _dict;  词频

d. 对特定单词的查询将返回出现该单词的所有行的行号。

e. 输出某单词所在的行文本时，程序必须能根据给定的行号从输入文件中获取相应的行。

示例：
使用提供的文件内容，然后查找单词 "element"。输出的前几行为：
---------------------------------------------
element occurs 125 times.
(line 62) element with a given key.
(line 64) second element with the same key.
(line 153) element |==| operator.
(line 250) the element type.
(line 398) corresponding element.
---------------------------------------------
*/

class TextQuery
{
public:
    void readFile(const string &filename){
        ifstream ifs(filename);
        if(!ifs){
            cerr<<"ifs is not good"<<endl;
            return;
        }

        string line;
        size_t idx=0;
        while(getline(ifs,line)){
            _textlines.push_back(line); //记录每一行
            istringstream iss(line);
            string word;
            while(iss >> word){
                string real_word=dealword(word);
                insert(real_word,idx);
            }
            ++idx;
        }
        ifs.close();
    }

    string dealword(const string &word){
        string processed_word;
        for(size_t idx=0;idx!=word.size();++idx){
            if(isalpha(word[idx])){
                processed_word += word[idx];
                //这里不能 return string();
                //因为这样会漏掉 diners, 这种在句尾的单词
                //应该把标点符号去掉，统计这个单词
                //但是这也存在一个问题，即会把(xiaoyage222)这种作为xiaoyag统计1次
            }
        }
        return processed_word;
    }

/*
    void store(const string &filename){
        ofstream ofs(filename);
        if(!ofs){
            cout<<"ofstream is not good"<<endl;
            return;
        }
        for(auto it=_textlines.begin(); it!= _textlines.end(); ++it){
            ofs<<*it<<endl;
        }
        ofs.close();
    }
    把要查询的文件另外存一个，暂时不懂有什么用
*/

    void insert(const string &word, size_t idx){
        //insert任务重，要记录词频和单词行号
        if(word==string())
            return;
        if(_dict.count(word)){
            ++_dict[word];
        }
        else{
            _dict[word]=1;
        }
        _wordlines[word].insert(idx);
    }

    void query(const string & word){
        if(!_dict.count(word))
            cout<<word<<" occurs 0 times."<<endl;
        else{
            cout<<word<<" occurs "<<_dict[word]<<" times."<<endl;
            for(auto it = _wordlines[word].begin();
                it!= _wordlines[word].end();
                ++it)
            {
                cout<<"(line "<<(*it)+1<<") "<<_textlines[*it]<<endl;
            }
        }
    }

private:
    vector<string> _textlines;  //原文
    map<string, set<int> > _wordlines;  //该词出现的所有行
    map<string, int> _dict; //单词出现的次数
};


int main()
{   
    TextQuery tq;
    tq.readFile("china_daily.txt");

    while(true){
        cout<<"enter word to look for, or q to quit: ";
        string s;
        if(!(cin >> s) || s=="q")
            break;
        tq.query(s);
        cout<<"============================================================="<<endl;
    }
    return 0;
}

